import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

/**
 * 
 * @author Luis F Cruz
 * 
 */
public class TreeStructure extends JPanel {

  /**
   * ******************* TREE RENDERER *********************************\
   * 
   * /** Class that overrides the tree cell renderer. It is needed for making it
   * draw the custom icons.
   * 
   * @author Luis F Cruz
   * 
   */
  private class CustomRenderer extends DefaultTreeCellRenderer {
    public CustomRenderer() {
      setBackgroundNonSelectionColor(TreeStructure.TREE_GREY_COLOR);
      rootIcon = new ImageIcon(
          TreeStructure.class.getResource("skin/tree_icon_root.png"));
      queryIcon = new ImageIcon(
          TreeStructure.class.getResource("skin/tree_icon_query.png"));
      browseIcon = new ImageIcon(
          TreeStructure.class.getResource("skin/tree_icon_browse.png"));
      similarIcon = new ImageIcon(
          TreeStructure.class.getResource("skin/tree_icon_similar.png"));
      itemIcon = new ImageIcon(
          TreeStructure.class.getResource("skin/tree_icon_item.png"));
      corpusIcon = new ImageIcon(
          TreeStructure.class.getResource("skin/tree_icon_corpus.png"));
    }

    public Component getTreeCellRendererComponent(JTree treeNode, Object value,
        boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {

      super.getTreeCellRendererComponent(treeNode, value, sel, expanded, leaf,
          row, hasFocus);

      switch (getNodeType(value)) {
        case TreeObject.ROOT_NODE:
          setIcon(rootIcon);
          setToolTipText("Search Results");
          break;
        case TreeObject.QUERY_NODE:
          setIcon(queryIcon);
          setToolTipText("Query Results");
          break;
        case TreeObject.BROWSE_NODE:
          setIcon(browseIcon);
          setToolTipText("Browse Results");
          break;
        case TreeObject.SIMILAR_NODE:
          setIcon(similarIcon);
          setToolTipText("Similar To Results");
          break;
        case TreeObject.ITEM_NODE:
          setIcon(itemIcon);
          break;
        case TreeObject.CORPUS_NODE:
          setIcon(corpusIcon);
          break;
      }
      return this;
    }

    private char getNodeType(Object value) {
      DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
      TreeObject nodeObject = (TreeObject) (node.getUserObject());
      return nodeObject.getType();
    }

    private ImageIcon browseIcon;
    private ImageIcon corpusIcon;
    private ImageIcon itemIcon;

    private ImageIcon queryIcon;

    private ImageIcon rootIcon;

    private ImageIcon similarIcon;
  }

  static final Color TREE_GREY_COLOR = new Color(232, 232, 232);

  private static void addPopup(Component component, final JPopupMenu popup) {
    component.addMouseListener(new MouseAdapter() {
      public void mousePressed(MouseEvent e) {
        if (e.isPopupTrigger())
          showMenu(e);
      }

      public void mouseReleased(MouseEvent e) {
        if (e.isPopupTrigger())
          showMenu(e);
      }

      private void showMenu(MouseEvent e) {
        popup.show(e.getComponent(), e.getX(), e.getY());
      }
    });
  }

  public TreeStructure(int type) {
    super();
    treeType = type;
    setLayout(new GridBagLayout());
    initTree();
    final GridBagConstraints gridBagConstraints = new GridBagConstraints();
    gridBagConstraints.fill = GridBagConstraints.BOTH;
    gridBagConstraints.weighty = 1.0;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.gridx = 0;
    add(tree, gridBagConstraints);
    expandAll(tree);
  }

  /**
   * Add Objects.
   * 
   * @param parent DefaultMutableTreeNode parent.
   * @param child Object child.
   * @return DefaultMutableTreeNode node.
   */
  public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent,
      Object child) {
    return addObject(parent, child, false);
  }

  /**
   * Add child to the currently selected node.
   * 
   * @param child Object child.
   * @return DefaultMutableTreeNode node.
   */
  public DefaultMutableTreeNode addObject(Object child) {
    DefaultMutableTreeNode parentNode = null;
    TreePath parentPath = tree.getSelectionPath();

    if (parentPath == null) {
      parentNode = topNode;
    } else {
      parentNode = (DefaultMutableTreeNode) (parentPath.getLastPathComponent());
    }
    return addObject(parentNode, child, true);
  }

  /**
   * Add to the query node.
   * 
   * @param object
   */
  public void addToTopNode(TreeObject object) {
    topNode.add(new DefaultMutableTreeNode(object));
  }

  /**
   * Clears all the search results from the tree.
   * 
   */
  public void clearAllResults() {
    topNode.removeAllChildren();
    treeModel.reload();
  }

  /**
   * Returns the current selected node
   * 
   * @return TreeObject
   */
  public TreeObject getSelectedNode() {
    DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
    if (node != null) {
      Object obj = node.getUserObject();
      if (obj != null) {
        return (TreeObject) obj;
      }
    }
    return null;
  }

  /**
   * Return the Parent node of the current selected node.
   * 
   * @return TreeObject
   */
  public TreeObject getSelectedNodeParent() {
    DefaultMutableTreeNode parentNode = null;
    TreePath parentPath = tree.getSelectionPath();
    parentNode = (DefaultMutableTreeNode) (parentPath.getLastPathComponent());
    TreeObject parent = (TreeObject) (parentNode.getParent());
    return (parent);
  }

  public void initializeBrowseTree(Corpus corpus) {
    Cluster topCluster = corpus.getTopCluster();
    addChildrenForCluster(topNode, topCluster);
    tree.expandRow(0);
  }

  /**
   * Remove the currently selected node.
   */
  public void removeCurrentSelectedNode() {
    TreePath currentSelection = tree.getSelectionPath();
    if (currentSelection != null) {
      DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) (currentSelection.getLastPathComponent());

      MutableTreeNode parent = (MutableTreeNode) (currentNode.getParent());
      if (parent != null) {
        treeModel.removeNodeFromParent(currentNode);
        return;
      }
    }
  }

  public void setQueryResults(CorpusFile[] results) {
    clearAllResults();
    if (results.length == 0) {
      addToTopNode(new TreeObject("no results", TreeObject.ITEM_NODE));
    }
    for (int i = 0; i < results.length; ++i) {
      addToTopNode(new CorpusTreeObject(results[i]));
    }
    tree.expandRow(0);
  }

  private void addChildrenForCluster(DefaultMutableTreeNode topNode,
      Cluster topCluster) {
    SuperCluster superCluster = topCluster.isSuperCluster();
    if (superCluster != null) {
      for (int i = 0;; ++i) {
        Cluster subcluster = superCluster.getSubcluster(i);
        if (subcluster == null) {
          break;
        }
        DefaultMutableTreeNode thisClusterNode = addObject(topNode,
            new TreeObject(subcluster.getDescriptor(), TreeObject.ITEM_NODE));
        addChildrenForCluster(thisClusterNode, subcluster);
      }
    } else {
      FileCluster fileCluster = (FileCluster) topCluster;
      String[] items = topCluster.getItems();
      for (int i = 0; i < items.length; ++i) {
        addObject(topNode, new CorpusTreeObject(fileCluster.getCorpusFile(i)));
      }
    }
  }

  /**
   * Add objects.
   * 
   * @param parentNode DefaultMutableTreeNode parent.
   * @param child Object child.
   * @param shouldBeVisible boolean visibility.
   * @return DefaultMutableTreeNode node.
   */
  private DefaultMutableTreeNode addObject(DefaultMutableTreeNode parentNode,
      Object child, boolean shouldBeVisible) {
    DefaultMutableTreeNode defNode;
    defNode = parentNode;

    DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(child);

    if (defNode == null) {
      defNode = topNode;
    }

    treeModel.insertNodeInto(childNode, defNode, defNode.getChildCount());

    // Make sure the user can see the new node.
    if (shouldBeVisible) {
      tree.scrollPathToVisible(new TreePath(childNode.getPath()));
    }
    return childNode;
  }

  /**
   * Expands all the nodes of the JTree
   * 
   * @param tree
   */
  private void expandAll(JTree tree) {
    int row = 0;
    while (row < tree.getRowCount()) {
      tree.expandRow(row);
      row++;
    }
  }

  /**
   * Populates the initial tree. Including the main nodes for each type of
   * search.
   */
  private void initTree() {
    if (treeType == TreePanel.BROWSE_TREE) {
      topNode = new DefaultMutableTreeNode(new TreeObject("",
          TreeObject.BROWSE_NODE));
    } else {
      topNode = new DefaultMutableTreeNode(new TreeObject("",
          TreeObject.QUERY_NODE));

    }

    treeModel = new DefaultTreeModel(topNode);
    tree = new JTree(treeModel);
    tree.setCellRenderer(new CustomRenderer());
    tree.getSelectionModel().setSelectionMode(
        TreeSelectionModel.SINGLE_TREE_SELECTION);
    tree.setShowsRootHandles(true);
    tree.setBackground(TREE_GREY_COLOR);
    tree.addTreeSelectionListener(new TreeSelectionListener() {
      public void valueChanged(TreeSelectionEvent e) {
        RABID.getMainFrame().updateButtons();
      }
    });

    // Adding popup menu
    final JPopupMenu popupMenu = new JPopupMenu();
    addPopup(tree, popupMenu);
    // Adding popup menu item
    final JMenuItem menuItem = new JMenuItem();
    menuItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent arg0) {
      }
    });
    menuItem.setText("Menu Item");
    popupMenu.add(menuItem);
  }

  private DefaultMutableTreeNode topNode;

  private JTree tree;

  private DefaultTreeModel treeModel;

  private int treeType;

}
